/*
 * Copyright 2003 Program of Computer Graphics, Cornell University
 *     580 Rhodes Hall
 *     Cornell University
 *     Ithaca NY 14853
 * Web: http://www.graphics.cornell.edu/
 * 
 * Not for commercial use. Do not redistribute without permission.
 */

#ifndef _H_TRIANGLE
#define _H_TRIANGLE

#include "Geometry.h"
#include <cstdio>

//our default type of geometry
class Triangle : public GeometryObject {
  friend class QuadPackedTriangleList;
private:

// The data must be properly aligned and the whole structure must
// fit in 64 bytes to avoid wasting space, so the layout
// if different in both architectures
#ifndef BRITE_64
  
  // 32 bit layout
  float v0x,v0y,v0z;
  float v1x,v1y,v1z;
  int mungedId;
  float v2x,v2y,v2z;
  class CompiledSharedTriangle *compiledTri; //pointer to compiled version is there is one

#else 

  // 64 bit layout
  class CompiledSharedTriangle *compiledTri; //pointer to compiled version is there is one
  BRITE_UNUSED float dummy1;
  float v0x,v0y,v0z;
  float v1x,v1y,v1z;
  int mungedId;
  float v2x,v2y,v2z;
  BRITE_UNUSED float dummy2;

#endif

public:
  Triangle() { }
  ~Triangle() {
     BRITE_DEBUGBREAK;  //geometry objects are now pool allocated and so their destructor should never be called
  }
  void set(float _v0x, float _v0y, float _v0z, float _v1x, float _v1y, float _v1z,
    float _v2x, float _v2y, float _v2z, int _id) {
      if (_id&~GID_VALUE_MASK) { printf("bad triangle id %d\n",_id);
      BRITE_DEBUGBREAK;
    }
    v0x = _v0x; v0y = _v0y; v0z = _v0z;
    v1x = _v1x; v1y = _v1y; v1z = _v1z;
    v2x = _v2x; v2y = _v2y; v2z = _v2z;
    mungedId = _id | GID_FLAG_FP; compiledTri = NULL;
  }
  static Triangle *poolAlloc(PoolAllocator *pool) {
    void *ptr = pool->allocate(sizeof(Triangle),SIMD_SIZE);
    return new(ptr) Triangle();
  }
  virtual int getType() const { return GEOM_TRIANGLE; }
  virtual PrimitiveList *compile(int num,const GeometryObject **array,PoolAllocator *pool)const;
  virtual bool intersectsBox(const AxisAlignedBox &box)const;
  virtual void expandBoxToInclude(AxisAlignedBox *box)const;
  //estimate the bounding box of the portion of the object inside the specified bounding box
  virtual AxisAlignedBox estimateIntersectionBounds(const AxisAlignedBox &box)const;
  //return approximate cost relative to a triangle
  virtual int estimateRelativeCost() const { return 1; }

  int id() const { return mungedId&GID_VALUE_MASK; }
  // get vertices as SIMD vectors of 4 floats (value of fourth float is not specified)
  // IMPORTANT: in a C++ class the first element is the pointer "this", but
  // it's size can be 32 or 64 bits!! So if we don't take care of that, the super
  // SIMD instructions here won't be properly aligned and the program will segfault
  F32vec4 getV0() const {
#ifndef BRITE_64
    F32vec4 temp = Load_F32vec4( (float*)this );
#else
    F32vec4 temp = Load_F32vec4(&this->dummy1);
#endif
    return _mm_shuffle_ps(temp,temp,_MM_SHUFFLE(3,3,2,1));
  }
  F32vec4 getV1() const { return Load_F32vec4(&this->v1x); }
  F32vec4 getV2() const { 
    F32vec4 temp = Load_F32vec4(&this->v2x);
    return _mm_shuffle_ps(temp,temp,_MM_SHUFFLE(2,2,1,0));
  }
  AxisAlignedBox getBoundingBox() const;
  bool intersectsRay(const Ray &ray, IntersectionRecord *hitRec) const;
  static void printSlotStats();
};


#ifdef USE_COMPILED_SHARED_TRIANGLE

class ALIGN16 CompiledSharedTriangle {
public:
  F32vec4 v0;  //note mangled id will be stored in fourth 32bit word of v0
  F32vec4 e1;
  F32vec4 e2;
  void set(Triangle *from) {
    F32vec4 mask = _mm_castsi128_ps(brite::VEC_0_N1_N1_N1);
    v0 = from->getV0()&mask;
    e1 = (from->getV1()&mask) - v0;
    e2 = (from->getV2()&mask) - v0;

    union {int i32; short i16[2]; } id = {from->id()};
    __m128i v0i = _mm_castps_si128(v0);
    v0i = _mm_insert_epi16(v0i, id.i16[0], 6);
    v0i = _mm_insert_epi16(v0i, id.i16[1], 7);
    v0  = _mm_castsi128_ps(v0i);
  }
};

class ALIGN16 QuadPackedTriangles {
public:
  CompiledSharedTriangle *tri[4];
  int getId(int index) {
    CompiledSharedTriangle *c = (CompiledSharedTriangle*)(PL_QUADPACKEDTRIANGLE_POINTER_MASK&(size_t)tri[index]);
    return *(int *)&(c->v0[3]);
  }
  CompiledSharedTriangle *getTri0Ptr() {
    return (CompiledSharedTriangle*)(PL_QUADPACKEDTRIANGLE_POINTER_MASK&(size_t)tri[0]);
  }
  int isLastQuadPack() {
    return static_cast<int>( PL_QUADPACKEDTRIANGLE_END&(size_t)tri[0] );
  }
  void setTriFlag() {
    tri[0] = (CompiledSharedTriangle*)(PL_QUADPACKEDTRIANGLE | KDST_LEAF | (size_t)tri[0]);
  }
  void setEndFlag() {
    tri[0] = (CompiledSharedTriangle*)(PL_QUADPACKEDTRIANGLE_END | (size_t)tri[0]);
  }
};

//in this case QUADPACKEDTRIANGLES are stored naked (without the usual PrimitiveList header)
//so they must be identified by special flags stored in the low bits of the first tri
//pointer in each quadpack
class QuadPackedTriangleList /*: public PrimitiveList*/ {
public:
  QuadPackedTriangles tris[1]; //actual number will be specified in length field

  static QuadPackedTriangleList*poolAlloc(int numTris, PoolAllocator *pool) {
    int size = /*sizeof(PrimitiveList) +*/ ((numTris+3)/4)*sizeof(QuadPackedTriangles);
    QuadPackedTriangleList *list = (QuadPackedTriangleList*)pool->allocate(size);
    memset(list,0,size);
//    list->length = numTris;
//    list->setType(GEOM_QUADPACKED_TRIANGLE);
    return list;
  }
  void setTriangle(int index, Triangle *from, PoolAllocator *pool);
  int computeLength() {
    int len = 0;
    CompiledSharedTriangle *first = tris->getTri0Ptr();
    for(int k=0;;k++) {
      len++;
      for(int j=1;j<4;j++) {
        if (tris[k].tri[j] == first) return len;
        len++;
      }
      if (tris[k].isLastQuadPack()) return len;
    }
  }

};

#else //USE_COMPILED_SHARED_TRIANGLE

//types used when we compile triangles into PrimitiveLists for fast intersection testing
class ALIGN16 QuadPackedTriangles {
public:
  F32vec4 e2x, e2y, e2z;
  F32vec4 e1x, e1y, e1z;
  F32vec4 v0x, v0y, v0z;
  Iu32vec4 id;
  int getId(int index) {
    return id[index];
  }
};

class QuadPackedTriangleList : public PrimitiveList {
public:
  QuadPackedTriangles tris[1]; //actual number will be specified in length field

  static QuadPackedTriangleList*poolAlloc(int numTris, PoolAllocator *pool) {
    int size = sizeof(PrimitiveList) + ((numTris+3)/4)*sizeof(QuadPackedTriangles);
    QuadPackedTriangleList *list = (QuadPackedTriangleList*)pool->allocate(size);
    memset(list,0,size);
    list->length = numTris;
    list->setType(GEOM_QUADPACKED_TRIANGLE);
    return list;
  }
  void setTriangle(int index, Triangle *from, PoolAllocator *pool);
  int computeLength() { return length; }
};

#endif //USE_COMPILED_SHARED_TRIANGLE


//note intersection routine for primitive list is in KDTree.cpp (to be in the same file as KDTree traversal for the compiler)
//void intersectPrimitiveLists(PrimitiveList *list, RayExt *ray, IntersectionRecord *hitRec, float cellTMin, float cellTMax);


#endif //_H_TRIANGLE

